<template>
  <el-table
    border
    ref="el_table"
    :data="sort_data"
    stripe
    style="width: 100%"
    :tree-props="props.treeProps"
    row-key="id"
  >
    <!-- 展开 -->
    <el-table-column v-if="$slots.expand" type="expand">
      <template #default="scope">
        <slot name="expand" :item="scope.row" :index="scope.$index"></slot>
      </template>
    </el-table-column>
    <!-- 多选 -->
    <el-table-column
      class-name="not-sortable"
      v-if="multiple_choice"
      type="selection"
      width="55"
      align="center"
    />
    <!-- 索引 -->
    <el-table-column type="index" v-if="isIndex && !$slots.index" />
    <!-- 自定义索引 -->
    <el-table-column v-if="isIndex && $slots.index" type="index" class-name="not-sortable">
      <template #default="scope">
        <slot name="index" :index="scope.$index + 1"></slot>
      </template>
    </el-table-column>
    <el-table-column
      v-for="(data, index) of getData"
      v-bind="data"
      show-overflow-tooltip
      :class-name="
        data.notSortable || data.fixed
          ? `${data.className ? `${data.className} not-sortable` : 'not-sortable'}`
          : `${data.className ? `${data.className} sortable` : 'sortable'}`
      "
      :key="index"
      :prop="getProp(drag_list.new_type_list[index], data).prop"
      :min-width="getProp(drag_list.new_type_list[index], data).minWidth"
      :align="data.align || 'center'"
    >
      <template v-if="getProp(drag_list.new_type_list[index], data).header_slot" #header>
        <slot :name="`header_${data.prop}`"></slot>
      </template>
      <template v-if="getProp(drag_list.new_type_list[index], data).isSlot" #default="scope">
        <slot
          :name="getProp(drag_list.new_type_list[index], data).prop"
          :item="scope.row"
          :index="scope.$index"
          :prop="getProp(drag_list.new_type_list[index], data).prop"
        ></slot>
      </template>
    </el-table-column>
  </el-table>
</template>
<script lang="ts" setup name="T_SorTable">
import { TablePropType, TableConfigurationType, PageType } from '@/interfaces/Element/Table.js'
import { ElTable } from 'element-plus'
import Sortable, { SortableEvent } from 'sortablejs'

/**
 * @return interface
 * **/
interface TableType {
  data: Array<unknown>
  tableType: Array<TablePropType>
  isDrag: TableConfigurationType // 是否拖拽以及拖拽配置
  multiple_choice?: boolean // 是否多选
  isIndex?: boolean
  row_draggable?: string
  isKeep?: boolean //是否默认在KeepAlive下使用
  treeProps?: { children: string; hasChildren: string }
  // eslint-disable-next-line no-unused-vars
  onRowDraggableEmit?: Sortable.Options //自定义行拖拽
  page?: PageType
}
interface DragType {
  solid_row_ele: null | Sortable
  solid_column_ele: null | Sortable
  old_type_list: Array<TablePropType>
  new_type_list: Array<TablePropType>
  new_data_list: Array<unknown>
}
/**
 * @return hooks
 * **/
const slots = useSlots()

/**
 * @return {props,emit}
 * **/
// 默认值
const props = withDefaults(defineProps<TableType>(), {
  data: () => [],
  multiple_choice: true, // 默认多选
  isIndex: false, // 默认无索引
  row_draggable: '.el-table__row',
  isDrag: () => {
    return {
      drag: 'row',
      get_new_data: () => {},
      reload: undefined
    }
  }
})

/**
 * @return {data,computed}
 * **/

const el_table = ref<InstanceType<typeof ElTable> | null>(null)
const drag_list = reactive<DragType>({
  solid_row_ele: null,
  solid_column_ele: null,
  old_type_list: [],
  new_type_list: [],
  new_data_list: []
})
const sort_data = computed(() => {
  if (props.page && props.page.currentPage && props.page.pageSize) {
    const result = props.data.slice(
      (props.page.currentPage - 1) * props.page.pageSize,
      props.page.currentPage * props.page.pageSize
    )
    return result
  }
  return props.data
})
const isTreeTable = computed(() => {
  if (props.onRowDraggableEmit) {
    return false
  }
  if (props.treeProps) {
    return props.data.some((item: any) => item[props.treeProps?.children!])
  }
  return props.data.some((item: any) => item.children)
})
const getData = computed<Array<TablePropType>>(() => {
  if (isTreeTable.value) {
    return props.tableType
  }
  return props.isDrag?.drag === 'row' ? props.tableType : drag_list.old_type_list
})
const getProp = computed(() => {
  return function (value: TablePropType, value2: TablePropType): TablePropType {
    if (isTreeTable.value) {
      return value2
    }
    return props.isDrag?.drag === 'row' ? value2 : value
  }
})

/**
 * @return {methods}
 * **/
//关闭所有expand
const closeAllExpand = () => {
  if (el_table.value) {
    sort_data.value.forEach(element => {
      el_table.value!.toggleRowExpansion(element, false)
    })
  }
}
//列拖拽
const columnDrop = () => {
  const wrapperTr = document.querySelector('.el-table__header-wrapper tr') as HTMLElement
  drag_list.solid_column_ele = Sortable.create(wrapperTr, {
    animation: 180,
    delay: 0,
    draggable: '.sortable',
    disabled: false,
    filter: '.not-sortable',
    onEnd: (e: SortableEvent) => {
      const { newIndex, oldIndex } = e
      let num: number | null = 1
      if (props.isIndex) {
        num += 1
      }
      if (slots.expand) {
        num += 1
      }
      const oldTabIndex = props.multiple_choice ? oldIndex! - num : oldIndex!
      const newTabIndex = props.multiple_choice ? newIndex! - num : newIndex!
      const oldItem = drag_list.new_type_list[oldTabIndex]
      drag_list.new_type_list.splice(oldTabIndex, 1)
      drag_list.new_type_list.splice(newTabIndex, 0, oldItem)
      props.isDrag?.get_new_data!(drag_list.new_type_list, 'column')
      num = null
    }
  })
}
//行拖拽
const rowDrop = (defaultObj: Sortable.Options | undefined) => {
  const tbody = document.querySelector('.el-table__body-wrapper tbody') as HTMLElement
  drag_list.solid_row_ele = Sortable.create(tbody, defaultObj)
}
const column_Sort = () => {
  drag_list.old_type_list = JSON.parse(JSON.stringify(props.tableType))
  drag_list.new_type_list = JSON.parse(JSON.stringify(props.tableType))
  columnDrop()
}
const row_Sort = () => {
  drag_list.new_data_list = JSON.parse(JSON.stringify(props.data))
  const defaultObj: Sortable.Options | undefined = props.onRowDraggableEmit
    ? props.onRowDraggableEmit
    : {
        animation: 180,
        draggable: props.row_draggable,
        filter: '.not-sortable',
        onEnd: (e: SortableEvent) => {
          const { newDraggableIndex, oldDraggableIndex } = e
          const currRow = drag_list.new_data_list.splice(oldDraggableIndex!, 1)[0]
          drag_list.new_data_list.splice(newDraggableIndex!, 0, currRow)
          props.isDrag?.get_new_data!(drag_list.new_data_list, 'row')
          if (props.isDrag.reload) {
            props.isDrag.reload()
          }
        }
      }
  rowDrop(defaultObj)
}
const initSortable = () => {
  if (isTreeTable.value && !props.onRowDraggableEmit) {
    return
  }
  switch (props!.isDrag?.drag) {
    case 'column':
      column_Sort()
      break
    case 'row':
      row_Sort()
      break
    case true:
      column_Sort()
      row_Sort()
      break
    default:
      break
  }
}
const unInstallSortable = () => {
  if (isTreeTable.value && !props.onRowDraggableEmit) {
    return
  }
  drag_list.old_type_list = []
  drag_list.new_type_list = []
  drag_list.new_data_list = []
  if (drag_list.solid_column_ele) {
    drag_list.solid_column_ele.destroy()
    drag_list.solid_column_ele = null
  }
  if (drag_list.solid_row_ele) {
    drag_list.solid_row_ele.destroy()
    drag_list.solid_row_ele = null
  }
}
onMounted(() => {
  initSortable()
})
onBeforeUnmount(() => {
  unInstallSortable()
})
defineExpose({ closeAllExpand })
</script>
